Conversation
|
Pushed
Retest on this PR branch after cleanup:
Results:
Remaining issue:
So the image/eRPC/storefront blockers are fixed on-branch, but I still think the multi-demo registration model needs another pass if the goal is to have several demos coexist on the storefront. |
|
Follow-up from local retest after layering the shared ERC-8004 registration fix from #363 on top of this PR branch. Original findings still stand for this PR as written:
I applied those fixes locally together with the shared-registration changes from #363 and reran the demos on the PR355 branch. Local retest result with both sets of changes appliedCommands:
Observed behavior after convergence:
Representative status change on non-owner offers after the #363 logic was layered in:
So the current read is:
|
5ad56d7 to
4646930
Compare
4646930 to
01bcd6a
Compare
Adds focused unit tests for paths left unverified in the sell-demo work. internal/demo (85.0% -> 94.6% statement coverage): - oracle_helpers_test.go: utilizationLabel tier boundaries (40/70/90), weiToGwei precision, hexToUint64/hexToBigInt, trimQuotes, safeDivFloat, computeGasStats (empty, single, multi-element, input-mutation safety). - rpc_errors_test.go: rpcCall branches for malformed body, JSON-RPC error field, HTTP 500 body with error JSON, non-JSON 200 body, transport failure, and the success path. - handlers_extra_test.go: OracleHandler early-return when eth_blockNumber fails (asserts no downstream RPCs fire, no stale fields leak), extractPayment default-status fallback + header passthrough, firstNonEmpty. internal/serviceoffercontroller/render_test.go: - buildServiceCatalogJSON excludes nil / paused / DeletionTimestamp / not-Ready offers from the public storefront. - deterministic alphabetical sort across multiple ready offers. - per-mtok pricing keeps PriceRaw empty but populates Price and Model. - fallback description autogenerated when Registration.Description empty. - baseURL with trailing slash is trimmed (no "//services" endpoints).
Added test coverage (
|
| Package | Before | After |
|---|---|---|
internal/demo |
85.0% | 94.6% |
internal/serviceoffercontroller |
18.4% | 18.8% |
The serviceoffercontroller number barely moved because most of that package is reconciler loops that need fake-client integration tests — the new branches lit up are the only statements that were reachable from pure unit tests.
1. internal/demo/oracle_helpers_test.go — pure math the oracle demo sells
The oracle demo's whole value is chain-analysis math, yet six helpers had zero direct tests before. A refactor that swaps pct > 90 for pct >= 90 would have silently mislabeled a congested block as "busy" for paying customers.
utilizationLabel tiers (now locked in)
──────────────────────────────────────
0 ──┐
│ "low"
40 ──┤ ← boundary tested at 40.0 AND 40.0001
│ "moderate"
70 ──┤ ← boundary tested at 70.0 AND 70.0001
│ "busy"
90 ──┤ ← boundary tested at 90.0 AND 90.0001
│ "congested"
100 ──┘
Also verified: weiToGwei precision (0, 1, 1e9, 1.5e9, 12.3457e9),
hexToUint64 (empty, 0x10, 10, 0xffff, 0xdeadbeef),
hexToBigInt (0x0, 0x10, ff, 0x3b9aca00),
trimQuotes (quoted, unquoted, "", `"`, empty),
safeDivFloat (incl. div-by-zero → 0),
computeGasStats (empty, single, multi-element, input-mutation).
2. internal/demo/rpc_errors_test.go — rpcCall error branches
Before this, only the success and transport-failure paths were covered. A JSON-RPC error field, a malformed body, or an HTTP 500 from eRPC all flowed through untested code.
rpcCall outcomes before after
───────────────── ────── ─────
Post fails (unreachable) ──→ YES YES
ReadAll fails ──→ no no (hard to trigger deterministically)
Unmarshal fails ──→ no YES ← "decode response"
rpcResp.Error != nil ──→ no YES ← "rpc error <code>: <msg>"
HTTP 500 + RPC err body ──→ no YES ← (verifier passes through)
success ──→ YES YES
3. internal/demo/handlers_extra_test.go — OracleHandler early-return + payment fallback
The oracle handler aborts to an errors-only body when eth_blockNumber fails — critical, because otherwise it would try to compute blocks from blockNum=0 and return nonsense. The existing tests all used a mock that never errored.
OracleHandler timeline (new test verifies early-return branch)
─────────────────────────────────────────────────────────────
chainId ── OK ─┐
│
blockNumber ── FAIL ──► respond(errors only) ──► return
↑ ↑
│ │
assert forbidden keys assert no further
absent from response RPC calls fire
(recentBlocks, (mock server fails
gasAnalysis, txVolume) the test if any
unexpected method
is called)
Also covers extractPayment default-status fallback (untested before — existing test always set the header), extractPayment header passthrough, and firstNonEmpty (first-wins, fall-through, all-empty, nil, single).
4. render_test.go — buildServiceCatalogJSON filter pipeline
Before: one happy-path + one empty test. The filter pipeline has four exclusion rules, none of which were directly verified — a regression on any of them exposes internal state on the public storefront.
offers (mixed)
│
▼
┌──────────────────────────────┐
│ nil? DeletionTimestamp? │ now tested — all 4 exclusions
│ paused annotation? │ exercised in a single table.
│ Ready condition != "True"? │
└──────────────────────────────┘
│
▼
sort by Name now tested — 3-offer reverse-order
│ input produces alphabetical output.
▼
┌──────────────────────────────┐
│ per-offer mapping: │
│ Description fallback │ now tested — empty Registration.
│ │ Description yields auto-generated
│ │ "x402 payment-gated <type> service".
│ per-mtok → Price non-empty, │
│ PriceRaw empty │ now tested.
│ baseURL trailing slash │ now tested — no //services endpoints.
└──────────────────────────────┘
Test run
$ go test -race -cover ./internal/demo/ ./internal/serviceoffercontroller/
ok github.com/ObolNetwork/obol-stack/internal/demo 1.482s coverage: 94.6%
ok github.com/ObolNetwork/obol-stack/internal/serviceoffercontroller 1.896s coverage: 18.8%
No changes to production code — tests only.
internal/demo: 94.6% -> 100.0% - oracle_errors_test.go: chainId RPC failure branch, per-block fetch error continue, malformed block JSON decode error, blocks.go latest-block fetch failure (errs append branch). - rpc_readbody_test.go: io.ReadAll error branch in rpcCall via a custom RoundTripper returning a response whose Body errors on Read. internal/serviceoffercontroller: 18.8% -> 29.5% - helpers_test.go: pure helpers in controller.go — truncateMessage, newBigInt, getenvDefault, httpRouteAccepted (Accepted/ResolvedRefs matrix), md5Sum, containsFinalizer, requestCleanupComplete, firstNonEmpty, decodeServiceOffer (default upstream ns, explicit ns, malformed), decodeRegistrationRequest, asUnstructured (incl. DeletedFinalStateUnknown tombstone), statusFor, loadRegistrationSigningKey (env, file, invalid hex, missing file), buildTombstoneRegistrationDocument, marshalRegistrationDocument (hash determinism), selectRegistrationOwner (zero-timestamp tiebreakers). - purchase_pure_test.go: hasStringInSlice, purchaseConditionIsTrue, setPurchaseCondition (append, update without status flip keeps LastTransitionTime, status flip bumps timestamp), normalizeRecoverySignature (v=0/1/27/28, malformed), normalizePurchasedUpstreamURL (suffix trimming), preSignedAuthMaps (empty + signature normalization roundtrip). - render_builders_test.go: buildMiddleware (ForwardAuth + headers + owner), buildRegistrationConfigMap, buildRegistrationDeployment (content-hash + label), buildRegistrationService, buildSkillCatalogConfigMap, buildSkillCatalogDeployment (api/services.json mount), buildSkillCatalogService, ownerRef / ownerRefFor, defaultString, describeOfferPrice (ladder), parseInt64, nonEmptyStringMap, fallbackOfferType, safeName extreme prefix/suffix clamp branch. No production code changes. All new tests pass under -race. Test files are new (no edits to existing *_test.go) to minimize merge-conflict surface with open PRs.
Public-release readiness: NOT YET — 3 blockersI took the three-commit PR ( The plumbing works — routing, 402 shape, handlers, storefront, JSON catalog, delete. But a first-time public user following the copy-paste flow will not see a working demo, for three reasons below. ✅ What I verified works
❌ Blockers for public release
|
Target v0.9.0, needs further testing and refinement (but committing for visibility)
Problem to be solved: 0 -> selling something takes too much knowledge for someone just checking the stack out
Proposed solution: some demo commands to make it easy to sell something basic